// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author : junglekim

#pragma once

#include <memory>
#include <unordered_map>
#include <vector>

#include <glog/logging.h>
#include <nlohmann/json.hpp>

#include "coin2/app/driver.h"
#include "coin2/exchange/base/feed/timer/deadline_timer.h"
#include "coin2/exchange/base/feed/timer/timer.h"
#include "coin2/exchange/base/feed/update.h"
#include "coin2/exchange/base/feed_util/book.h"
#include "coin2/strategy/strategy.h"
#include "coin2/strategy/util/reporter2.h"
#include "coin2/strategy/vmm/vmm_arbitrageur.h"
#include "coin2/strategy/vmm/vmm_order_controller.h"
#include "coin2/strategy/vmm/vmm_pricer.h"

using coin::proto::OrderGatewayLog;
using coin2::app::impl::Driver;
using coin2::exchange::base::feed::FeedTimer;
using coin2::exchange::base::feed::FeedUpdate;
using coin2::exchange::base::feed::timer::DeadlineTimer;
using coin2::exchange::base::order::OrderUpdate;
using coin2::exchange::order::SimConfig;
using coin2::strategy::util::StrategyReporter2;
using google::protobuf::util::JsonStringToMessage;

namespace coin2::strategy::vmm {

class VmmStrategy : public coin2::strategy::IStrategy {
 public:
  VmmStrategy(const std::string& strat_json, const std::optional<int64_t>& ts, const bool& verbose);

  void Init(Driver* driver) override;
  void UpdateSimConfig(SimConfig* config);
  void onBookFeed(const FeedUpdate&) override;
  void onTradeFeed(const FeedUpdate&) override;
  void onHeartbeatFeed(const FeedTimer&) override;
  void onAccountInfo(const OrderUpdate&) override;
  void onAccountOrder(const OrderUpdate&) override;
  void onOrderLog(const OrderGatewayLog&) override;
  void onCleanup(Driver* driver) final;
  void onCleanupAux(Driver* driver, int);

  void UpdateTimestamp(int64_t ts) {
    if (ts > timestamp_) {
      timestamp_ = ts;
    } else {
      timestamp_++;
    }
  }

 protected:
  nlohmann::json config_;
  Driver* driver_{nullptr};
  int64_t start_time_;
  bool verbose_;
  int64_t timestamp_{0};
  int64_t last_print_ts_{0};
  int64_t last_opt_report_ts_{0};
  int64_t last_filled_ts_{0};
  bool dry_report_{false};
  bool dry_run_{false};
  bool avoid_arbi_{false};
  int timer_freeze_ms_;
  std::unordered_map<std::string, int> latency_;
  std::vector<std::unique_ptr<IProduct>> products_;
  std::unique_ptr<VmmPricer> pricer_;
  std::unique_ptr<VmmOrderController> controller_;
  std::unique_ptr<VmmArbitrageur> arbitrageur_;
  std::unique_ptr<StrategyReporter2> strat_reporter_{nullptr};
  std::unique_ptr<DeadlineTimer> feed_timer_;
  std::unordered_map<std::string, std::unique_ptr<DeadlineTimer>> con_timers_;
  std::vector<std::pair<TimerType, std::string>> timer_map_;
  std::unordered_map<const IProduct*, double> reserve_map_;
  std::unordered_map<std::string, PassiveExecutorConfig> exe_configs_;
  std::unordered_map<std::string, std::unordered_map<std::string, ::nlohmann::json>> opt_configs_;
  std::unordered_map<std::string, std::string> opt_owners_;

  bool update_orders_in_progress_ = false;
};
}  // namespace coin2::strategy::vmm
